package l2

import (
	"zstackgo/pkg/core/cloudbus"
	"zstackgo/pkg/core/db"
	"zstackgo/pkg/core/errorcode"
	"zstackgo/pkg/header/apimediator"
	herrorcode "zstackgo/pkg/header/errorcode"
	hmessage "zstackgo/pkg/header/message"
	hl2 "zstackgo/pkg/header/network/l2"
	"zstackgo/spring"
)

var (
	_ apimediator.ApiMessageInterceptor = new(L2NetworkApiInterceptor)
)

func init() {
	o := new(L2NetworkApiInterceptor)
	spring.Register(o, "L2NetworkApiInterceptor", []*spring.PluginExtension{
		{
			Interface: "header.apimediator.ApiMessageInterceptor",
		},
	})
}

type L2NetworkApiInterceptor struct {
	bus  cloudbus.CloudBus     `autowire:"CloudBusImpl3"`
	dbf  db.DatabaseFacade     `autowire:"DatabaseFacadeImpl"`
	errf errorcode.ErrorFacade `autowire:"ErrorFacadeImpl"`
}

func (i *L2NetworkApiInterceptor) SetServiceId(msg hmessage.APIMessage) {
	l2msg, ok := msg.(hl2.L2NetworkMessage)
	if ok {
		i.bus.MakeTargetServiceIdByResourceUuidMsg(msg, hl2.SERVICE_ID, l2msg.GetL2NetworkUuid())
	}
}

func (i *L2NetworkApiInterceptor) Intercept(msg hmessage.APIMessage) (hmessage.APIMessage, error) {
	var err error

	if tmp, ok := msg.(*hl2.APICreateL2NetworkMsg); ok {
		err = i.ValidateAPICreateL2NetworkMsg(tmp)
	}
	if tmp, ok := msg.(*hl2.APIDeleteL2NetworkMsg); ok {
		err = i.ValidateAPIDeleteL2NetworkMsg(tmp)
	}
	if tmp, ok := msg.(*hl2.APIDetachL2NetworkFromClusterMsg); ok {
		err = i.ValidateAPIDetachL2NetworkFromClusterMsg(tmp)
	}
	if tmp, ok := msg.(*hl2.APIAttachL2NetworkToClusterMsg); ok {
		err = i.ValidateAPIAttachL2NetworkToClusterMsg(tmp)
	}
	if err != nil {
		return nil, err
	}

	i.SetServiceId(msg)
	return msg, nil
}

func (i *L2NetworkApiInterceptor) ValidateAPIAttachL2NetworkToClusterMsg(msg *hl2.APIAttachL2NetworkToClusterMsg) error {
	var count int64
	err := db.NewQ().Model(hl2.ModelL2NetworkClusterRefVO).Where("ClusterUuid = ? and L2NetworkUuid = ?",
		msg.ClusterUuid, msg.L2NetworkUuid).Count(&count).Error
	if err != nil {
		return err
	}

	if count > 0 {
		apimediator.NewApiMessageInterceptionException(herrorcode.OpErr("l2Network[uuid:%s] has attached to cluster[uuid:%s], can't attach again", msg.L2NetworkUuid, msg.ClusterUuid))
	}

	return nil
}

func (i *L2NetworkApiInterceptor) ValidateAPIDetachL2NetworkFromClusterMsg(msg *hl2.APIDetachL2NetworkFromClusterMsg) error {
	var count int64
	err := db.NewQ().Model(hl2.ModelL2NetworkClusterRefVO).Where("ClusterUuid = ? and L2NetworkUuid = ?",
		msg.ClusterUuid, msg.L2NetworkUuid).Count(&count).Error
	if err != nil {
		return err
	}

	if count == 0 {
		apimediator.NewApiMessageInterceptionException(herrorcode.OpErr("l2Network[uuid:%s] has not attached to cluster[uuid:%s]", msg.L2NetworkUuid, msg.ClusterUuid))
	}

	return nil
}

func (i *L2NetworkApiInterceptor) ValidateAPIDeleteL2NetworkMsg(msg *hl2.APIDeleteL2NetworkMsg) error {
	var count int64
	err := db.NewQ().Model(hl2.ModelL2NetworkVO).Where("Uuid = ?",
		msg.Uuid).Count(&count).Error
	if err != nil {
		return err
	}

	if count == 0 {
		evt := hl2.NewAPIDeleteL2NetworkEvent(msg.Uuid)
		i.bus.Publish(evt)

		return &apimediator.StopRoutingException{}
	}

	return nil
}

func (i *L2NetworkApiInterceptor) ValidateAPICreateL2NetworkMsg(msg *hl2.APICreateL2NetworkMsg) error {
	if !hl2.HasTypeL2Network(msg.Type) {
		return apimediator.NewApiMessageInterceptionException(herrorcode.ArgErr("unsupported l2Network type[%s]", msg.Type))
	}

	// once we already created a linux bridge with a physical interface,
	// we can not use it to create a OvsDpdk bridge
	var count int64
	err := db.NewQ().Model(hl2.ModelL2NetworkVO).Where("PhysicalInterface = ? and VSwitchType <> ?",
		msg.PhysicalInterface, msg.VSwitchType).Count(&count).Error
	if err != nil {
		return err
	}

	if count > 0 {
		apimediator.NewApiMessageInterceptionException(herrorcode.ArgErr("can not create %s L2Network with physicalInterface:[%s] which was already been used by another vSwitchType.",
			msg.VSwitchType, msg.PhysicalInterface))
	}

	return nil
}
